package com.alien.admin.modules.basic.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alien.admin.modules.deploy.domain.DeployRec;
import com.alien.admin.modules.deploy.domain.DpServerConfig;
import com.alien.admin.utils.TemplateStrUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @description: 容器部署模板生成器
 *  {
 * 	"resource": {
 * 		"requests": {
 * 			"cpu": "200m",
 * 			"memory": "512Mi"
 *                },
 * 		"limits": {
 * 			"cpu": "2000m",
 * 			"memory": "2Gi"
 *        }    * 	},
 * 	"healthcheck": {
 * 		"livenessDelaySeconds": 60,
 * 		"readinessDelaySeconds": 60,
 * 		"periodSeconds": 10,
 * 		"path": "/version.txt"    * 	},
 * 	"ports": [{
 * 		"name": "server",
 * 		"containerPort": 38000,
 * 		"protocol": "TCP"    * 	}, {
 * 		"name": "dubbo",
 * 		"containerPort": 38001,
 * 		"protocol": "TCP"    * 	}, {
 * 		"name": "jvmdebug",
 * 		"containerPort": 38002,
 * 		"protocol": "TCP"    * 	}],
 * 	"volumes": [{
 * 		"name": "logs-volumes",
 * 		"claimName": "devnfslog"    * 	}, {
 * 		"name": "apm-volumes",
 * 		"claimName": "pvcapm"    * 	}],
 * 	"volumeMounts": [{
 * 		"mountPath": "/root/logs",
 * 		"name": "logs-volumes",
 * 		"readOnly": false,
 * 		"subPathExpr": ""    * 	}, {
 * 		"mountPath": "/app/agent/",
 * 		"name": "apm-volumes",
 * 		"readOnly": true,
 * 		"subPathExpr": "apm"    * 	}]
 * }
 * @author: zhouseyi
 * @date: 2021/9/28 17:44
 * @version: 1.0
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
public class K8sTemplateGenerator {

    DpServerConfig dpServerConfig;
    String deploymentTplContent;
    DeployRec deployRec;
    Map<String, String> envMap;

    private final String CONFIG_MAP_NAME = "env-config";

    public String generateYmlContent(Map<String, Object> userParamMap, String imageUrl) {
        // 从dpServerConfig中获取
        String namespace = dpServerConfig.getK8sNameSpace();
        String serverParamStr = dpServerConfig.getDeployParam();

        Map<String, Object> serverParamMap = generateYmlParamMap(serverParamStr);
        serverParamMap.put("namespace", namespace);
        serverParamMap.put("project", deployRec.getProjectName().toLowerCase());
        serverParamMap.put("branch", deployRec.getTargetBranch());
        serverParamMap.put("imageUrl", imageUrl);

        // 替换环境变量
        List<String> envMapLineList = generateKeyMapStr(envMap, namespace);
        if(envMapLineList == null) {
            serverParamMap.put("CONFIG_MAP_CONFIG", "");
            serverParamMap.put("ENV_CONFIG", "");
        } else {
            serverParamMap.put("CONFIG_MAP_CONFIG", envMapLineList.get(0));
            serverParamMap.put("ENV_CONFIG", envMapLineList.get(1));
        }

        // 用户自定义
        serverParamMap.putAll(userParamMap);

        String ymlContent = TemplateStrUtil.generate(deploymentTplContent, serverParamMap);
        return ymlContent;
    }


    public Map<String, Object> generateYmlParamMap(String paramStr) {
        Map<String, Object> paramMap = new HashMap<>(10);

        JSONObject paramObj = JSON.parseObject(paramStr);
        Object replicasObj = paramObj.getOrDefault("replicas", 1);
        Integer replicas = Integer.valueOf(String.valueOf(replicasObj));
        paramMap.put("replicas", replicas);

        JSONArray volumesArr = paramObj.getJSONArray("volumes");
        String volumesStr = generateVolumesConfigStr(volumesArr);
        paramMap.put("VOLUMES_CONFIG", volumesStr);

        JSONArray volumeMountedArr = paramObj.getJSONArray("volumeMounts");
        String volumeMountStr = generateVolumesMountedConfigStr(volumeMountedArr);
        paramMap.put("VOLUMEMOUNTS_CONFIG", volumeMountStr);

        JSONArray portArr = paramObj.getJSONArray("ports");
        String portStr = generatePortConfigStr(portArr);
        paramMap.put("PORTS_CONFIG", portStr);

        JSONObject healthCheckObj = paramObj.getJSONObject("healthcheck");
        String healthStr = generateHealthCheckStr(healthCheckObj);
        paramMap.put("HEALTHCHECK_CONFIG", healthStr);

        JSONObject resourceObj = paramObj.getJSONObject("resource");
        String resourceStr = generateResourceStr(resourceObj);
        paramMap.put("RESOURCES_CONFIG", resourceStr);

        return paramMap;
    }

    public String generateResourceStr(JSONObject resourceObj) {
        if(resourceObj == null) {
            return "";
        }

        JSONObject requestsObj = resourceObj.getJSONObject("requests");
        String requestCPU = requestsObj.getString("cpu");
        String requestMemory = requestsObj.getString("memory");


        JSONObject limitsObj = resourceObj.getJSONObject("limits");
        String limitCPU = limitsObj.getString("cpu");
        String limitMemory = limitsObj.getString("memory");

        int paddingNum = 8;
        StringBuilder resStr = new StringBuilder();
        resStr.append(padCode("resources:", paddingNum));

        resStr.append(padCode("requests:", paddingNum + 2));
        resStr.append(padCode(String.format("cpu: %s", requestCPU), paddingNum + 4));
        resStr.append(padCode(String.format("memory: %s", requestMemory), paddingNum + 4));

        resStr.append(padCode("limits:", paddingNum + 2));
        resStr.append(padCode(String.format("cpu: %s", limitCPU), paddingNum + 4));
        resStr.append(padCode(String.format("memory: %s", limitMemory), paddingNum + 4));

        return resStr.toString();
    }

    public String generateHealthCheckStr(JSONObject healthCheckObj) {
        if(healthCheckObj == null) {
            return "";
        }

        Integer livenessDelaySeconds = healthCheckObj.getInteger("livenessDelaySeconds");
        Integer readinessDelaySeconds = healthCheckObj.getInteger("readinessDelaySeconds");
        Integer periodSeconds = healthCheckObj.getInteger("periodSeconds");
        String path = healthCheckObj.getString("path");

        int paddingNum = 8;
        StringBuilder resStr = new StringBuilder();
        resStr.append(padCode("livenessProbe:", paddingNum));

        String livenessDelaySecondsLine = String.format("initialDelaySeconds: %d", livenessDelaySeconds);
        resStr.append(padCode(livenessDelaySecondsLine, paddingNum + 2));

        String periodSecondsLine = String.format("periodSeconds: %d", periodSeconds);
        resStr.append(padCode(periodSecondsLine, paddingNum + 2));

        resStr.append(padCode("httpGet:", paddingNum + 2));
        resStr.append(padCode("port: server", paddingNum + 4));

        String pathLine = String.format("path: %s", path);
        resStr.append(padCode(pathLine, paddingNum + 4));

        resStr.append(padCode("readinessProbe:", paddingNum));

        String readinessDelaySecondsLine = String.format("initialDelaySeconds: %d", readinessDelaySeconds);
        resStr.append(padCode(readinessDelaySecondsLine, paddingNum + 2));

        resStr.append(padCode(periodSecondsLine, paddingNum + 2));
        resStr.append(padCode("httpGet:", paddingNum + 2));
        resStr.append(padCode("port: server", paddingNum + 4));
        resStr.append(padCode(pathLine, paddingNum + 4));

        return resStr.toString();
    }

    public String generatePortConfigStr(JSONArray portsArr) {
        if(portsArr == null || portsArr.isEmpty()) {
            return "";
        }

        int paddingNum = 8;
        StringBuilder resStr = new StringBuilder();
        resStr.append(padCode("ports:", paddingNum));

        int size = portsArr.size();
        for(int index = 0; index < size; index++) {
            JSONObject volumeObj = portsArr.getJSONObject(index);
            String name = volumeObj.getString("name");
            Integer containerPort = volumeObj.getInteger("containerPort");
            String protocol = volumeObj.getString("protocol");

            String nameLine = String.format("- name: %s", name);
            resStr.append(padCode(nameLine, paddingNum));

            String containerPortLine = String.format("containerPort: %d", containerPort);
            resStr.append(padCode(containerPortLine, paddingNum + 2));

            String protocolLine = String.format("protocol: %s", protocol);
            resStr.append(padCode(protocolLine, paddingNum + 2));
        }

        return resStr.toString();
    }

    public String generateVolumesMountedConfigStr(JSONArray volumeMountArr) {
        if(volumeMountArr == null || volumeMountArr.isEmpty()) {
            return "";
        }

        int paddingNum = 8;
        StringBuilder volumesPaddingStr = new StringBuilder();
        volumesPaddingStr.append(padCode("volumeMounts:", paddingNum));

        int size = volumeMountArr.size();
        for(int index = 0; index < size; index++) {
            JSONObject volumeObj = volumeMountArr.getJSONObject(index);
            String name = volumeObj.getString("name");
            String mountPath = volumeObj.getString("mountPath");
            String subPathExpr = volumeObj.getString("subPathExpr");
            Boolean readOnly = volumeObj.getBoolean("readOnly");

            String mountPathLine = String.format("- mountPath: %s", mountPath);
            volumesPaddingStr.append(padCode(mountPathLine, paddingNum));

            String nameLine = String.format("name: %s", name);
            volumesPaddingStr.append(padCode(nameLine, paddingNum + 2));

            String readOnlyLine = String.format("readOnly: %b", readOnly);
            volumesPaddingStr.append(padCode(readOnlyLine, paddingNum + 2));

            String subPathExprLine = String.format("subPathExpr: %s", subPathExpr);
            volumesPaddingStr.append(padCode(subPathExprLine, paddingNum + 2));
        }

        return volumesPaddingStr.toString();
    }

    public String generateVolumesConfigStr(JSONArray volumesArr) {
        if(volumesArr == null || volumesArr.isEmpty()) {
            return "";
        }

        int paddingNum = 6;
        StringBuilder volumesPaddingStr = new StringBuilder();
        volumesPaddingStr.append(padCode("volumes:", paddingNum));

        int size = volumesArr.size();
        for(int index = 0; index < size; index++) {
            JSONObject volumeObj = volumesArr.getJSONObject(index);
            String name = volumeObj.getString("name");
            String claimName = volumeObj.getString("claimName");

            String nameLine = String.format("- name: %s", name);
            volumesPaddingStr.append(padCode(nameLine, paddingNum));
            volumesPaddingStr.append(padCode("persistentVolumeClaim:", paddingNum + 2));

            String claimNameLine = String.format("claimName: %s", claimName);
            volumesPaddingStr.append(padCode(claimNameLine, paddingNum + 4));
        }

        return volumesPaddingStr.toString();
    }

    public List<String> generateKeyMapStr(Map<String, String> keyMap, String namespace) {
        if(keyMap == null || keyMap.isEmpty()) {
            return null;
        }

        int paddingNum = 8;
        List<String> keyMapStrList = new ArrayList<>(2);

        StringBuilder definitionCol = new StringBuilder("apiVersion: v1\n" +
                "kind: ConfigMap\n" +
                "metadata:\n" +
                "  name: env-config\n" +
                "  namespace: " + namespace + "\n" +
                "data:\n");
        StringBuilder envCol = new StringBuilder();

        keyMap.keySet().forEach(key -> {
            String definitionStr = String.format("%s: \"%s\"", key, keyMap.get(key));
            definitionCol.append(padCode(definitionStr, 2));

            String nameLine = padCode(String.format("- name: %s", key), paddingNum);
            String valueFormLine = padCode("valueFrom:", paddingNum + 2);
            String configMapKeyRefLine = padCode("configMapKeyRef:", paddingNum + 4);
            String keyNameLine = padCode(String.format("name: %s", CONFIG_MAP_NAME), paddingNum + 6);
            String keyLine = padCode(String.format("key: %s", key), paddingNum + 6);

            envCol.append(nameLine)
                    .append(valueFormLine)
                    .append(configMapKeyRefLine)
                    .append(keyNameLine)
                    .append(keyLine);
        });

        definitionCol.append("\n---");

        keyMapStrList.add(definitionCol.toString());
        keyMapStrList.add(envCol.toString());
        return keyMapStrList;
    }

    public static String padCode(String str, int num) {
        StringBuilder resBuilder = new StringBuilder();
        for(int i = 0; i < num; i++) {
            resBuilder.append(" ");
        }

        resBuilder.append(str);
        resBuilder.append("\n");
        return resBuilder.toString();
    }
}
